#include <sys/types.h>
#include <cpu.h>
#include <debug.h>
#include <page_manager.h>

/* 
 * Some explanation about the bz page management
 * 
 * The bz-kernel is loaded at 1MB, and it will reserve
 * all addresses between 1MB and 4MB for the kernel.
 * The page directory, will start at 4MB and will be
 * 4MB long. 
 * 
 * 
 * The addresses below 1MB will be untoucheable, while
 * the kernel, and the page directory (from 1MB to 8MB-1kb)
 * will be mapped to 3GB, and address from 8MB above
 * will be mapped to 1MB.
 * 
 */

#define ONE_KB		0x400
#define FOUR_KB 	0x1000
#define ONE_MB	 	0x100000
#define FOUR_MB 	0x400000
#define SEVEN_MB	0x700000
#define EIGHT_MB 	0x800000
#define THREE_GB 	0xC0000000

//1073741824d - 1GB

const ulong_t PageManager::PAGE_DIRECTORY_ADDRESS = FOUR_MB - FOUR_KB;
const ulong_t PageManager::PAGE_TABLE_ADDRESS = (PAGE_DIRECTORY_ADDRESS + FOUR_KB);
const ulong_t PageManager::PAGE_SIZE = FOUR_KB;
const ulong_t PageManager::PAGING_BIT = 1 << 31;

void PageManager::initPaging(uint_t memSize, uint_t kernelStart, uint_t kernelEnd) {
	ulong_t *pageDir = (ulong_t*) PAGE_DIRECTORY_ADDRESS;
	ulong_t *pageTable = (ulong_t *) PAGE_TABLE_ADDRESS;
	
//	int totalPages = calculatePagesNumber(memSize);
//	int kernelPages = calculatePagesNumber(SEVEN_MB);
//	int availablePages = totalPages - kernelPages;
	
	// non-kernel pages
	for(int j = 0; j < 2; j++) {
		ulong_t address = 0;
		createPageTable(pageTable,address);
		pageDir[j] = (ulong_t) pageTable | 3;
		pageTable = (ulong_t*)((ulong_t)pageTable + FOUR_KB);
	}
	// kernel pages
	for (int i = 2; i < 1024; i++) {
		pageDir[i] = 0 | 2;					// Not present
	}	

	
	// TODO Remove it - DEBUG
	Debug::dprint("page directory * $");
	Debug::dprint(*pageDir,'x');
	Debug::dprint((ulong_t)pageDir,'x');
	Debug::dprint("page table * $");
	Debug::dprint(*pageTable,'x');
	Debug::dprint((ulong_t)pageTable,'x');
	// ---
	enablePaging((ulong_t) pageDir);
	
	if ( getCR0() > PAGING_BIT ) 
		Debug::dprint("paging ok!");
	else
		Debug::dprint("paging NOT ok!");
}

void PageManager::createPageTable(ulong_t* pageTable, ulong_t address) {
	// TODO Remove it - DEBUG
	Debug::dprint("page table $ - method");
	Debug::dprint((ulong_t)pageTable,'x');
	// ---
	for (int i = 0; i < 1024; i++) {
		pageTable[i] = address | 3;		// Supervisor, R/W, Present
		address += PAGE_SIZE; 
	}
}

/* 1 page maps PAGE_SIZE
 * x pages maps memSize
 * x = (memSize/PAGE_SIZE).
 */
uint_t PageManager::calculatePagesNumber(uint_t memSize) {
	return memSize/PAGE_SIZE;
}

/* Points the CR3 to the given page directory
 * and enables page by setting the CR0.PE bit.
 */
void PageManager::enablePaging(ulong_t pageDirectory) {
		setCR3(pageDirectory); 		// put that page directory address into CR3
		ulong_t cr0 = getCR0(); 	// gets CR0 value
		cr0 |= PAGING_BIT;			// enables the PG bit (bit[31])
		setCR0(cr0); 				// sets the cr0 value
}
